//
// CImageGrabberFilter.cpp
//

#include "stdafx.h"
#include "CImageGrabberFilter.h"

#include <initguid.h>
#include "GrabberIds.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

////////////////////////////////////////////////////////////////////////
CImageGrabberFilter::CImageGrabberFilter(IGraphBuilder * inGraph) : 
CDXFilter(inGraph, CLSID_HQImageGrabber, "Image Grabber")
{
	mIGrabber = NULL;
}

CImageGrabberFilter::~CImageGrabberFilter(void)
{
	ReleaseFilter();
}

BOOL CImageGrabberFilter::CreateFilter(void)
{
	if (CDXFilter::CreateFilter())
	{
		HRESULT hr = mFilter->QueryInterface(IID_IImageGrabber, 
			(void**) &mIGrabber);
		return SUCCEEDED(hr);
	}
	return FALSE;
}

void CImageGrabberFilter::ReleaseFilter(void)
{
	if (mIGrabber)
	{
		mIGrabber->Release();
		mIGrabber = NULL;
	}

	CDXFilter::ReleaseFilter();
}

BOOL CImageGrabberFilter::GetImageSize(long * outWidth, 
									   long * outHeight, 
									   long * outBitCount)
{
	if (mIGrabber)
	{
		HRESULT hr = mIGrabber->get_ImageSize(outWidth, 
			outHeight, outBitCount);
		return SUCCEEDED(hr);
	}
	return FALSE;
}

BOOL CImageGrabberFilter::GetFrameSize(long * outFrameSize)
{
	if (mIGrabber)
	{
		HRESULT hr = mIGrabber->get_FrameSize(outFrameSize);
		return SUCCEEDED(hr);
	}
	return FALSE;
}

BOOL CImageGrabberFilter::GetBitmapInfoHeader(BITMAPINFOHEADER * outBitmapInfo)
{
	if (mIGrabber)
	{
		HRESULT hr = mIGrabber->get_BitmapInfoHeader(outBitmapInfo);
		return SUCCEEDED(hr);
	}
	return FALSE;
}

BOOL CImageGrabberFilter::Is16BitsRGB(long * outIsRGB565)
{
	if (mIGrabber)
	{
		HRESULT hr = mIGrabber->get_Is16BitsRGB(outIsRGB565);
		return SUCCEEDED(hr);
	}
	return FALSE;
}

BOOL CImageGrabberFilter::SetFieldPicture(BOOL inIsField)
{
	if (mIGrabber)
	{
		HRESULT hr = mIGrabber->put_IsFieldPicture(inIsField);
		return SUCCEEDED(hr);
	}
	return FALSE;
}

BOOL CImageGrabberFilter::Snapshot(BYTE * outBuffer, BOOL inIsSyncMode)
{
	if (mIGrabber)
	{
		HRESULT hr = mIGrabber->Snapshot(outBuffer, inIsSyncMode);
		return SUCCEEDED(hr);
	}
	return FALSE;
}

BOOL CImageGrabberFilter::IsAsyncSnapshotFinished(void)
{
	BOOL finished = TRUE;
	if (mIGrabber)
	{
		mIGrabber->IsAsyncSnapshotFinished(&finished); 
	}
	return finished;
}

BOOL CImageGrabberFilter::CancelAllPending(void)
{
	if (mIGrabber)
	{
		HRESULT hr = mIGrabber->CancelAllPending();
		return SUCCEEDED(hr);
	}
	return FALSE;
}

//#define _ASYNC_SNAPSHOT
// Demo: how to use these interface methods?
BOOL CImageGrabberFilter::SnapshotToFile(const char * inFile)
{
	long frameSize = 0;
	if (GetFrameSize(&frameSize))
	{
		BYTE * pBuffer = new BYTE[frameSize];

#ifndef _ASYNC_SNAPSHOT
		if (Snapshot(pBuffer, TRUE))
		{
			WriteImageFile(pBuffer, frameSize, inFile);
		}
#else
		Snapshot(pBuffer, FALSE);
		long counter = 0;
		while (!IsAsyncSnapshotFinished())
		{
			Sleep(2);
			// We should not wait a long time in the Main App thread!
			// Suppose we can only wait 2 seconds
			if (++counter > 1000)
			{
				break;
			}
		}

		if (counter > 1000)
		{
			CancelAllPending();
		}
		else
		{
			WriteImageFile(pBuffer, frameSize, inFile);
		}
#endif
		if (pBuffer)
		{
			delete[] pBuffer;
			pBuffer = NULL;
		}
		return TRUE;
	}
	return FALSE;
}

void CImageGrabberFilter::WriteImageFile(BYTE * inData, 
										 long inSize, 
										 const char * inFile)
{
	BITMAPINFOHEADER info;
	GetBitmapInfoHeader(&info);
	int nColors = 1 << info.biBitCount;
	if (nColors > 256)
	{
		nColors = 0;
	}

	long isRGB565 = 0;
	BOOL is16Bits = Is16BitsRGB(&isRGB565);
	const DWORD bits555[] = {0x7C00,0x03E0,0x001F};
	const DWORD bits565[] = {0xF800,0x07E0,0x001F};

	BITMAPFILEHEADER	hdr;
	hdr.bfType		= ((WORD) ('M' << 8) | 'B');	// always is "BM"
	hdr.bfSize		= inSize + sizeof(hdr) + sizeof(BITMAPINFOHEADER);
	hdr.bfReserved1 = 0;
	hdr.bfReserved2 = 0;
	hdr.bfOffBits	= (DWORD) (sizeof(BITMAPFILEHEADER) + info.biSize + nColors * sizeof(RGBQUAD));
	
	if (is16Bits)
	{
		hdr.bfSize    += 12;
		hdr.bfOffBits += 12;
	}

#ifdef _DEBUG
	int offset1 = sizeof(BITMAPFILEHEADER);
	int offset2 = sizeof(BITMAPINFOHEADER);
#endif 

	FILE * fp = fopen(inFile, "wb");
	if (fp != NULL)
	{
		fwrite(&hdr, sizeof(char), sizeof(BITMAPFILEHEADER), fp);
		fwrite(&info, sizeof(char), sizeof(BITMAPINFOHEADER), fp);
		// Here, if nColors less than 256, need write RGBQUAD
		// ...
		if (is16Bits)
		{
			if (isRGB565 == 1) // RGB565
			{
				fwrite(bits565, sizeof(DWORD), 3, fp);
			}
			else  // RGB555
			{
				fwrite(bits555, sizeof(DWORD), 3, fp);
			}
		}

		fwrite(inData, sizeof(char), inSize, fp);
		fclose(fp);
	}
}